﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.Windows.Forms;

using Imaging = System.Drawing.Imaging;
using OpenTK.Graphics.OpenGL;

namespace GLSL
{
    public class Texture
    {
        private static Dictionary<string, Texture> textureDictionary = new Dictionary<string, Texture>();

        public int ID { get; private set; }
        public int width { get; private set; }
        public int height { get; private set; }
        public string filename { get; private set; }
        public static bool IsFloatTextureSupported = true;

        /// <summary>
        /// Creates new texture and assigns TextureID
        /// </summary>
        /// <param name="filename">Image File</param>
        private Texture(string filename)
        {
            try
            {
                Bitmap bitmap = new Bitmap(filename);

                this.ID = GL.GenTexture();
                GL.BindTexture(TextureTarget.Texture2D, this.ID);

                Imaging.BitmapData data = bitmap.LockBits(
                    new Rectangle(0, 0, bitmap.Width, bitmap.Height),
                    Imaging.ImageLockMode.ReadOnly,
                    Imaging.PixelFormat.Format32bppArgb);

                this.width = bitmap.Width;
                this.height = bitmap.Height;
                this.filename = filename;

                GL.TexImage2D(TextureTarget.Texture2D, 0,
                    PixelInternalFormat.Rgba, bitmap.Width, bitmap.Height, 0,
                    PixelFormat.Bgra, PixelType.UnsignedByte, data.Scan0);

                bitmap.UnlockBits(data);
                bitmap.Dispose();

                GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.Nearest);
                GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)TextureMagFilter.Nearest);

                GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapS, (int)TextureWrapMode.ClampToBorder);
                GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapT, (int)TextureWrapMode.ClampToBorder);
            }
            catch (Exception)
            {
                MessageBox.Show(string.Format("Error finding the texture file '{0}'," + Environment.NewLine
                    + "Please check that the file is in the same Directory", filename),
                    "GSLS Texture Error",
                    MessageBoxButtons.OK,
                    MessageBoxIcon.Error       
                    );           
            }
        }

        /// <summary>
        /// Removes texture from texture dictionary and GL textures
        /// </summary>
        /// <param name="filename">Image File</param>
        public void Dispose()
        {
            Texture texture;
            if (textureDictionary.TryGetValue(this.filename, out texture))
            {
                textureDictionary.Remove(filename);
                GL.DeleteTexture(texture.ID);
            }
        }

        /// <summary>
        /// Binds the texture to the GL buffer
        /// </summary>
        public void bind()
        {
            GL.BindTexture(TextureTarget.Texture2D, this.ID);
        }

        /// <summary>
        /// Gets the associated texture glass from the image file
        /// </summary>
        /// <param name="filename">Image File</param>
        /// <returns>Texture Class</returns>
        public static Texture getTexture(string filename)
        {
            Texture texture = null;
            textureDictionary.TryGetValue(filename, out texture);
            return texture;
        }

        /// <summary>
        /// Sets the image as a texture and adds it to the texture dictionary
        /// </summary>
        /// <param name="filename">Image File</param>
        public static void initialize(string filename)
        {
            Texture texture = new Texture(filename);
            textureDictionary.Add(filename, texture);
        }

        public void GenerateMipmap()
        {
            //Display.Texture = this;
            //GL.GenerateMipmap((GenerateMipmapTarget)Target);
        }

        /// <summary>
        /// Removes all textures from memory
        /// </summary>
        public static void DisposeAll()
        {
            List<string> tex = new List<string>();

            foreach (KeyValuePair<string, Texture> dta in textureDictionary)
            {
                tex.Add(dta.Key);
            }

            for (int i = 0; i < tex.Count; i++)
            {
                textureDictionary[tex[i]].Dispose();
            }

            textureDictionary.Clear();
        }
    }
}

